/* ====================================================================
 * (#)Header: BeanUtils.java
 * (#)Revision: 
 * (#)Date Created: Mar 2, 2007 12:07:22 AM
 * (#)Created By: Karol Wozniak
 * ====================================================================
 *
 * Copyright (c) 2007 karolak .  All rights reserved.
 *
 * ====================================================================
 * 
 * The software application represented here is the sole property of 
 * karolak. Its unauthorized utilization is expressly prohibited.
 * 
 * ====================================================================
 */
package com.karolak.bean;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import com.google.gson.Gson;

/**
 * <code>BeanUtils</code> is used to ....
 * 
 * @author <a href="mailto:kwozniak@hotmail.com">Karol Wozniak</a>
 */
public class BeanUtils {

	private static Gson gson = new Gson();

	// === public static utility api === //
	public static boolean equals(Object o1, Object o2) {
		if (o1 == null && o2 == null) {
			return true;
		}
		if (o1 == null) {
			return false;
		}
		if (o2 == null) {
			return false;
		}
		return o1.equals(o2);
	}

	public static boolean equals(String str1, String str2) {
		if (str1 == null && str2 == null) {
			return true;
		}
		if (str1 == null) {
			return false;
		}
		if (str2 == null) {
			return false;
		}
		return str1.trim().equals(str2.trim());
	}

	// === get === //
	@SuppressWarnings("unchecked")
	public static <T> T getPropertyValue(Object bean, String propertyName, Object... params) {
		Object value = null;
		if (bean == null || propertyName == null || propertyName.trim().length() == 0) {
			return (T) value;
		}

		String[] tokens = propertyName.trim().split("\\.");
		value = bean;
		for (int i = 0; i < tokens.length; i++) {
			String token = tokens[i];

			if (value == null) {
				break;
			}
			if (value instanceof Map) {
				value = ((Map<Object, Object>) value).get(token);
			} else {
				Class<?> clazz = value.getClass();
				if (i == tokens.length - 1) {
					Class<?>[] paramTypes = null;
					if (params != null && params.length > 0) {
						paramTypes = new Class<?>[params.length];
						for (int j = 0; j < params.length; j++) {
							paramTypes[j] = params[j].getClass();
						}
					}
					Method propertyAccessor = getPropertyAccessor(clazz, token, paramTypes);
					value = getPropertyValue(value, propertyAccessor, params);
				} else {
					Method propertyAccessor = getPropertyAccessor(clazz, token);
					value = getPropertyValue(value, propertyAccessor);
				}
			}
		}
		return (T) value;
	}

	public static Object getPropertyValue(Object bean, Method propertyAccessor, Object... params) {
		Object propertyValue = null;
		if (bean == null || propertyAccessor == null) {
			return propertyValue;
		}
		try {
			if (params == null) {
				params = new Object[] {};
			}
			propertyValue = propertyAccessor.invoke(bean, params);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return propertyValue;
	}

	public static Method[] getPropertyAccessors(Class<?> clazz, String... propertyNames) {
		Method[] propertyAccessors = {};
		if (clazz == null || propertyNames == null || propertyNames.length == 0) {
			return propertyAccessors;
		}
		List<Method> list = new ArrayList<Method>();
		for (String propertyName : propertyNames) {
			Method method = getPropertyAccessor(clazz, propertyName);
			if (method != null) {
				list.add(method);
			}
		}
		propertyAccessors = list.toArray(propertyAccessors);
		return propertyAccessors;
	}

	public static <T> Method getPropertyAccessor(Class<T> clazz, String propertyName, Class<?>... paramTypes) {
		Method propertyAccessor = null;
		if (clazz == null || propertyName == null) {
			return propertyAccessor;
		}
		try {
			if (paramTypes == null) {
				paramTypes = new Class[] {};
			}
			propertyAccessor = clazz.getMethod(getPropertyAccessorName(propertyName), paramTypes);
		} catch (Exception e) {
			System.err.println(e.getMessage());
			try {
				propertyAccessor = clazz.getMethod(getBooleanPropertyAccessorName(propertyName), new Class[] {});
			} catch (Exception e2) {
				System.err.println(e2.getMessage());
			}
		}
		return propertyAccessor;
	}

	public static String toString(Object object) {
		if (object == null) {
			return null;
		}
		String json = getGson().toJson(object);
		json = json.replace("\"", "");
		String clsName = object.getClass().getSimpleName();
		return String.format("%s%s", clsName, json);
	}

	public static <T extends Enum<T>> T getEnumValue(Class<T> typeClass, String name) {
		return Enum.valueOf(typeClass, name);
	}

	public static Gson getGson() {
		return gson;
	}

	// === create accessor names === //
	private static String getPropertyAccessorName(String propertyName) {
		return "get" + capitalizeFirstCharacter(propertyName);
	}

	private static String getBooleanPropertyAccessorName(String propertyName) {
		return "is" + capitalizeFirstCharacter(propertyName);
	}

	private static String capitalizeFirstCharacter(String propertyName) {

		if (propertyName == null) {
			return propertyName;
		}
		if (propertyName.length() == 1) {
			return propertyName.toUpperCase();
		}
		return propertyName.substring(0, 1).toUpperCase() + propertyName.substring(1);
	}
}
